//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#ifndef _SERIAL_CONTROL_H_
#define _SERIAL_CONTROL_H_

#include "serialio.h"

// Set of functions which handle top level I/O functions
typedef struct
{
    SERIAL_ErrNo (*write)(
        io_handle_t handle,
        const void *buf,
        Int32 *len);

    SERIAL_ErrNo (*read)(
        io_handle_t handle,
        void *buf,
        Int32 *len);

    SERIAL_ErrNo (*get_config)(
        io_handle_t handle,
        Int32 key,
        void *buf,
        Int32 *len);

    SERIAL_ErrNo (*set_config)(
        io_handle_t handle,
        Int32 key,
        const void *buf,
        Int32 *len);

    SERIAL_ErrNo (*wait_event)(
        io_handle_t handle,
        int *mask);
} DeviceIOTbl;

// Initialization macros

#define DEVIO_TABLE(_l,_write,_read,_get_config,_set_config, _wait_event) \
DeviceIOTbl _l = {                                              \
    _write,                                                     \
    _read,                                                      \
    _get_config,                                                \
    _set_config,                                                \
    _wait_event,                                                \
};

// Serial I/O interfaces

typedef struct serial_channel serial_channel;
typedef struct serial_funs serial_funs;

// The block transfer request functions may fail for one of two
// reasons. It's important for the caller to know which.
typedef enum
{
    RCV_OK,
    RCV_FULL,
    RCV_DISABLED
} rcv_req_reply_t;

typedef enum
{
    XMT_OK,
    XMT_EMPTY,
    XMT_DISABLED
} xmt_req_reply_t;

// Pointers into upper-level driver which interrupt handlers need
typedef struct
{
    // Initialize the channel
    void (*serial_init)(serial_channel *chan);

    // Cause an additional character to be output if one is available
    void (*xmt_char)(serial_channel *chan);
    // Consume an input character
    void (*rcv_char)(serial_channel *chan, unsigned char c);

    // Request space for input characters
    rcv_req_reply_t (*data_rcv_req)(serial_channel *chan, int avail,
                                    int* space_avail, unsigned char** space);
    // Receive operation completed
    void (*data_rcv_done)(serial_channel *chan, int chars_rcvd);
    // Request characters for transmission
    xmt_req_reply_t (*data_xmt_req)(serial_channel *chan, int space,
                                    int* chars_avail, unsigned char** chars);
    // Transmit operation completed
    void (*data_xmt_done)(serial_channel *chan, int chars_sent);

    void (*indicate_status)(serial_channel *chan, serial_line_status_t *s);
} serial_callbacks_t;

#define SERIAL_CALLBACKS(_l,_init,_xmt_char,_rcv_char, \
                           _data_rcv_req, _data_rcv_done, \
                           _data_xmt_req, _data_xmt_done, \
                           _indicate_status)  \
serial_callbacks_t _l = {                               \
    _init,                                              \
    _xmt_char,                                          \
    _rcv_char,                                          \
    _data_rcv_req,                                      \
    _data_rcv_done,                                     \
    _data_xmt_req,                                      \
    _data_xmt_done,                                     \
    _indicate_status                                    \
};

extern serial_callbacks_t g_serial_callbacks;

typedef struct cbuf_t
{
    cbuf_t() : wait(TRUE, UNSIGNALED) {};

    unsigned char           *data;
    volatile int             put;
    volatile int             get;
    int                      len;         // if not exist the cache buffers(
                                          // len==0), use non-interrupt mode
    int                      low_water;   // For tx: min space in
                                          // buffer before restart For
                                          // rx: max buffer used
                                          // before flow unthrottled

    int                      high_water;  // For tx: unused For rx:
                                          // min buffer used before
                                          // throttle
    drv_event_t              wait;
    drv_mutex_t              lock;
    bool                     waiting;
    volatile bool            abort;       // Set by an outsider to
                                          // kill processing
    volatile Int32           pending;     // This many bytes waiting
                                          // to be sent
} cbuf_t;

typedef struct flow_desc_t
{
    flow_desc_t() : flags(0), flowctrl_software(false), xchar(0) {}
    UInt32 flags;
    bool   flowctrl_software;
    UInt8  xchar; /* XON/XOFF character */
} flow_desc_t;

typedef struct wait_comm_event_t
{
    wait_comm_event_t() : wait(TRUE, UNSIGNALED) {};

    int mask;       /* event mask, for set/get mask */
    int value;      /* current event value */
    drv_event_t wait;
    int waiting;    /* wait event mask, prevent invalid signal*/
} wait_comm_event_t;

typedef struct serial_extension
{
    serial_extension() : device_opened(false) {}
    bool device_opened;
    SERIAL_TIMEOUTS timeouts;
} serial_extension;

typedef struct serial_private
{
    serial_private() : interrupt_stat(0) {}
    Int32 interrupt_stat;
} serial_private;

// Private data which describes this channel
struct serial_channel
{
    serial_channel() : status_callback(0), status_callback_priv(0) {}
    serial_funs        *funs;
    serial_callbacks_t *callbacks;
    void               *dev_priv;  // Whatever is needed by actual
                                   // device routines
    SERIAL_INFO         config;    // Current configuration

    bool                bufs_init;
    cbuf_t              out_cbuf;
    cbuf_t              in_cbuf;

    bool                support_flow_control;
    flow_desc_t         flow_desc;

    bool                support_line_status;
    serial_line_status_callback_fn_t status_callback;
    SERIAL_ADDRWORD     status_callback_priv;

    wait_comm_event_t   comm_event;

    serial_extension    extension;
    serial_private      priv;
};

// Flow descriptor flag values
#define SERIAL_FLOW_OUT_THROTTLED     (1<<0)
#define SERIAL_FLOW_IN_THROTTLED      (1<<1)

// Low level interface functions
struct serial_funs
{
    // enable uart
    bool (*open)(serial_channel *);
    // diable uart
    bool (*close)(serial_channel *);

    // Send one character to the output device, return true if consumed
    bool (*putc)(serial_channel *priv, unsigned char c);

    // Fetch one character from the device
    bool (*getc)(serial_channel *priv, unsigned char *c);

    // Change hardware configuration (baud rate, etc)
    SERIAL_ErrNo (*set_config)(
        serial_channel *priv,
        Int32 key,
        const void *xbuf,
        Int32 *len);

    // Enable the transmit channel and turn on transmit interrupts
    void (*start_xmit)(serial_channel *priv);

    // Disable the transmit channel and turn transmit interrupts off
    void (*stop_xmit)(serial_channel *priv);

    // isr
    int (*isr)(serial_channel *priv);
};

#define SERIAL_FUNS(_l, \
    _open, _close, _putc,_getc,_set_config,_start_xmit,_stop_xmit,_isr) \
serial_funs _l = {                                                      \
    _open,                                                              \
    _close,                                                             \
    _putc,                                                              \
    _getc,                                                              \
    _set_config,                                                        \
    _start_xmit,                                                        \
    _stop_xmit,                                                         \
  _isr                                                                  \
};                                                                      \

// top entry of device control table

typedef struct devtab_entry
{
    DeviceIOTbl     *handlers;

    bool            (*init)(struct devtab_entry *tab);

    SERIAL_ErrNo    (*lookup)( /* reserved function*/
        struct devtab_entry **tab,
        struct devtab_entry *sub_tab,
        const char *name);

    void            *priv;
} devtab_entry;

#define DEVTAB_ENTRY(_l,_handlers,_init,_lookup,_priv)  \
devtab_entry _l = {                   \
    _handlers,                                                           \
    _init,                                                               \
    _lookup,                                                             \
    _priv,                                                               \
};

typedef struct
{
    uint_t num_of_port;
    struct
    {
        ioport_t iobase;
        irq_t irq;
    } port[4];
} port_tbl_t;

extern port_tbl_t g_port_tbl;
extern DeviceIOTbl g_serial_devio;

bool serial_device_entry(
    devtab_entry * tab,
    int port_base,
    int irq);

#endif // _SERIAL_CONTROL_H_
